<?php

namespace App\Models;

use App\Exceptions\InvalidRequestException;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model as BaseModel;
use Illuminate\Support\Facades\Schema;

/**
 * App\Models\Model
 *
 * @method static \Illuminate\Database\Eloquent\Builder|Model newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Model newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Model orderCheck($request)
 * @method static \Illuminate\Database\Eloquent\Builder|Model pageCheck($request, $field = [], $makeVisible = [])
 * @method static \Illuminate\Database\Eloquent\Builder|Model query()
 * @mixin \Eloquent
 */
class Model extends BaseModel
{
    use HasFactory;

    /**
     * 分页：每页做大条数
     * @var int
     */
    public $maxPerPage = 50;

    public $defaultPage = 20;

    private static $_instance;

    /**
     * 实例化
     * @return mixed|static
     */
    public static function model()
    {
        $class = get_called_class();

        if (!isset(self::$_instance[$class])) {
            self::$_instance[$class] = new static();
        }

        return self::$_instance[$class];
    }

    /**
     * 修改查询后的时间格式转化
     * @param \DateTimeInterface $date
     * @return string
     */
    protected function serializeDate(\DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

    /**
     * 简单查找一条数据
     * @param array $where
     * @return mixed
     */
    public function simpleFindOne($where=[], $field = ['*'])
    {
        return $this->where($where)->first($field);
    }

    /**
     * 公共新增 返回自增id
     * @param array $data   数据 一维数组
     * @param array $other  需要添加又不在模型白名单中的数据 一维数组
     * @return int|void     自增id
     * @throws InvalidRequestException
     */
    public function storeReturnIncId($data=array(), $other=array())
    {
        if(!$data || !is_array($data)){
            return;
        }

        $tmp = $this->fill($data)->toArray();

        $id = 0;
        if($tmp && $insertData = array_merge($tmp,$other)){
            $id = $this->insertGetId($insertData);
        }

        if(!$id){
            throw new InvalidRequestException("创建失败,请重试...");
        }
        return $id;
    }

    /**
     * 批量添加
     * @param array $data       数据，二维数组 注意传递的键名是否和数据库一致的问题 不一致需处理一致后再调用此方法
     * @param array $other      需要添加又不在模型白名单中的数据 一维数组
     * @throws InvalidRequestException
     */
    public function multiStore($data=array(),$other=array())
    {
        if(!$data || !is_array($data)){
            return;
        }
        $insertData = array();
        foreach($data as $k=>$v){
            if($v && $tmp = $this->fill($v)->toArray()){
                $insertData[] = array_merge($tmp,$other);
            }
        }
        if($insertData){
            if(!$this->insert($insertData)){
                throw new InvalidRequestException('批量创建失败,请重试...');
            }
        }
    }

    /**
     * 公共排序规则作用域
     * @param $query
     * @param $request
     * @return mixed
     */
    public function scopeOrderCheck($query,$request)
    {
        $order = $this->primaryKey;
        $sort  = 'desc';
        //如果是流加载方式 只允许按主键降序
        if (!isset($request['minId']) || !$request['minId']) {
            $sort  = (isset($request['sort']) && $request['sort'] && in_array(strtolower($request['sort']),['asc','desc'])) ? $request['sort'] : 'desc';
            if (isset($request['order']) && $request['order']) {
                $allowField = Schema::getColumnListing($this->table);
                in_array($request['order'],$allowField) && $order = $request['order'];
            }
        }
        return $query->orderBy($order,$sort);
    }

    /**
     * 公共动态作用域 必须分页
     * @param $request
     * @param $field  查询的具体字段
     * @param $makeVisible  如果模型设置了隐藏字段,但是需要展示可以传入这个数组中
     */
    public function scopePageCheck($query,$request,$field=array("*"),$makeVisible=array())
    {
        $limit = (isset($request['limit']) && $request['limit']) ? min($this->maxPerPage,$request['limit']) : $this->defaultPage;
        $page  = (isset($request['page']) && $request['page']) ? $request['page'] : 1;
        //流式分页加载参数
        $minId = (isset($request['minId']) && $request['minId']) ? true : false;
        if ($minId) {
            $page = 1;
        }

        if ($makeVisible) {
            $data = $query->paginate($limit,$field,'page',$page);
            $data->data = $data->makeVisible($makeVisible);
            $data = $data->toArray();
        } else {
            $data = $query->paginate($limit,$field,'page',$page)->toArray();
        }
        //如果是流式分页 则处理分页参数
        if ($minId && $data['data']) {
            $data['current_page'] = (int)$request['page'];
            $data['total'] = ($request['page']-1) * $limit + $data['total'];
            $data['last_page'] = ceil($data['total'] / $limit);
            $data['to'] = $request['page'] * $limit;
            $data['from'] = $data['to'] - $limit +1;
            $data['last_page_url'] = $data['path'] . '?page=' . $data['last_page'];
            $data['prev_page_url'] = $data['path'] . '?page=' . ($data['current_page'] > 1 ?  $data['current_page'] - 1 : 'null');
            $data['next_page_url'] = $data['path'] . '?page=' . ($data['last_page'] > $request['page'] ? $data['current_page'] + 1 : 'null');
        }

        return $this->dealPage($data);

    }

    /**
     * 处理分类后的返回结构
     * @param $data
     * @return array
     */
    public function dealPage($data)
    {
        return [
            'data'  => $data['data'],
            'links' => [
                'first' => $data['first_page_url'],
                'last'  => $data['last_page_url'],
                'prev'  => $data['prev_page_url'],
                'next'  => $data['next_page_url']
            ],
            'meta'  => [
                'current_page'  => $data['current_page'],
                'from'          => $data['from'],
                'last_page'     => $data['last_page'],
                'path'          => $data['path'],
                'per_page'      => $data['per_page'],
                'to'            => $data['to'],
                'total'         => $data['total']
            ]
        ];
    }
}
